/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "xmlfile.h"
#include "node/util/string.h"
using Tokens = ::node::util::Tokens;

namespace node {
    namespace util {

        XmlFile::XmlFile(void)
            : doc_()
            , filename_("")
        {

        }

        XmlFile::~XmlFile(void)
        {

        }

        XmlFile* XmlFile::Create(const char* filename, 
            const char* version, const char* encoding, const char* standalone)
        {
            XmlFile* doc = new XmlFile;

            if (doc)
            {
                TiXmlDeclaration* declare = new TiXmlDeclaration(version, encoding, standalone);
                if (declare)
                {
                    doc->doc_.LinkEndChild(declare);
                    doc->filename_ = filename;
                    return doc;
                }

                delete doc;
            }

            return NULL;
        }

        XmlFile* XmlFile::Open(const char* filename, TiXmlEncoding encoding /* = TIXML_DEFAULT_ENCODING */)
        {
            XmlFile* doc = new XmlFile;
            if (doc)
            {
                if (doc->LoadFile(filename, encoding))
                {
                    doc->filename_ = filename;
                    return doc;
                }
                delete doc;
            }
            return NULL;
        }

        XmlFile::Node* XmlFile::CreateRootNode(const char* name)
        {
            if (doc_.RootElement() != NULL)
                return NULL;

            Node* node = CreateNode(name);
            doc_.LinkEndChild(node);
            return node;
        }

        XmlFile::Node* XmlFile::Find(const char* path)
        {
            Tokens tokens = ::node::util::StrSplit(path, ".");
            Node* node = GetRootNode();
            Tokens::const_iterator it = tokens.begin();
            for (; it != tokens.end(); ++it)
            {
                node = node->FirstChildElement(it->c_str());
                if (node == NULL)
                    break;
            }
            return node;
        }

        XmlFile::Node* XmlFile::FindOrCreate(const char* path)
        {
            Tokens tokens = ::node::util::StrSplit(path, ".");
            Node* node = GetRootNode();
            Tokens::const_iterator it = tokens.begin();
            for (; it != tokens.end(); ++it)
            {
                Node* tmp = node->FirstChildElement(it->c_str());
                if (tmp == NULL)
                {
                    tmp = CreateNode(it->c_str());
                    if (tmp)
                    {
                        node->LinkEndChild(tmp);
                        node = tmp;
                        continue;
                    }
                    return NULL;
                }
                else
                {
                    node = tmp;
                }
            }
            return node;
        }

        const char* XmlFile::GetText(const char* path, const char* default /* = "" */)
        {
            Node* node = Find(path);
            if (node)
            {
                TiXmlNode* text = node->FirstChild();
                if (text->Type() == TiXmlNode::TINYXML_TEXT)
                {
                    return text->ToText()->Value();
                }
            }
            return default;
        }

        int XmlFile::GetInt(const char* path, int default /* = 0 */)
        {
            Node* node = Find(path);
            if (node)
            {
                TiXmlNode* text = node->FirstChild();
                if (text->Type() == TiXmlNode::TINYXML_TEXT)
                {
                    return atoi(text->ToText()->Value());
                }
            }
            return default;
        }

        long XmlFile::GetLong(const char* path, long default /* = 0 */)
        {
            Node* node = Find(path);
            if (node)
            {
                TiXmlNode* text = node->FirstChild();
                if (text->Type() == TiXmlNode::TINYXML_TEXT)
                {
                    return atol(text->ToText()->Value());
                }
            }
            return default;
        }

        double XmlFile::GetDouble(const char* path, double default /* = 0.0f */)
        {
            Node* node = Find(path);
            if (node)
            {
                TiXmlNode* text = node->FirstChild();
                if (text->Type() == TiXmlNode::TINYXML_TEXT)
                {
                    return atof(text->ToText()->Value());
                }
            }
            return default;
        }

        bool XmlFile::SetText(const char* path, const char* value)
        {
            Node* node = FindOrCreate(path);
            if (node)
            {
                TiXmlNode* text = node->FirstChild();
                if (text == NULL)
                {
                    text = new Text(value);
                    if (text)
                    {
                        node->LinkEndChild(text);
                        return true;
                    }
                }
                else if (text->Type() == TiXmlNode::TINYXML_TEXT)
                {
                    text->ToText()->SetValue(value);
                    return true;
                }
            }
            return false;
        }

        bool XmlFile::SetInt(const char* path, int value)
        {
            char buf[32] = { 0 };
            Node* node = FindOrCreate(path);
            if (node)
            {
                TiXmlNode* text = node->FirstChild();
                if (text == NULL)
                {
                    ::node::util::itoa(value, buf, sizeof(buf), 10);
                    text = new Text(buf);
                    if (text)
                    {
                        node->LinkEndChild(text);
                        return true;
                    }
                }
                else if (text->Type() == TiXmlNode::TINYXML_TEXT)
                {
                    ::node::util::itoa(value, buf, sizeof(buf), 10);
                    text->ToText()->SetValue(buf);
                    return true;
                }
            }
            return false;
        }

        bool XmlFile::SetLong(const char* path, long value)
        {
            char buf[32] = { 0 };
            Node* node = FindOrCreate(path);
            if (node)
            {
                TiXmlNode* text = node->FirstChild();
                if (text == NULL)
                {
                    ::node::util::ltoa(value, buf, sizeof(buf), 10);
                    text = new Text(buf);
                    if (text)
                    {
                        node->LinkEndChild(text);
                        return true;
                    }
                }
                else if (text->Type() == TiXmlNode::TINYXML_TEXT)
                {
                    ::node::util::ltoa(value, buf, sizeof(buf), 10);
                    text->ToText()->SetValue(buf);
                    return true;
                }
            }
            return false;
        }

        bool XmlFile::SetDouble(const char* path, double value)
        {
            char buf[1024] = { 0 };
            Node* node = FindOrCreate(path);
            if (node)
            {
                TiXmlNode* text = node->FirstChild();
                if (text == NULL)
                {
                    ::node::util::snprintf(buf, sizeof(buf), "%f", value);
                    text = new Text(buf);
                    if (text)
                    {
                        node->LinkEndChild(text);
                        return true;
                    }
                }
                else if (text->Type() == TiXmlNode::TINYXML_TEXT)
                {
                    ::node::util::snprintf(buf, sizeof(buf), "%f", value);
                    text->ToText()->SetValue(buf);
                    return true;
                }
            }
            return false;
        }

    }
}